home *** CD-ROM | disk | FTP | other *** search
/ Sprite 1984 - 1993 / Sprite 1984 - 1993.iso / src / lib / tk2.3 / dist / tkPlace.c < prev    next >
Encoding:
C/C++ Source or Header  |  1992-06-10  |  26.9 KB  |  940 lines

  1. /* 
  2.  * tkPlace.c --
  3.  *
  4.  *    This file contains code to implement a simple geometry manager
  5.  *    for Tk based on absolute placement or "rubber-sheet" placement.
  6.  *
  7.  * Copyright 1992 Regents of the University of California
  8.  * Permission to use, copy, modify, and distribute this
  9.  * software and its documentation for any purpose and without
  10.  * fee is hereby granted, provided that this copyright
  11.  * notice appears in all copies.  The University of California
  12.  * makes no representations about the suitability of this
  13.  * software for any purpose.  It is provided "as is" without
  14.  * express or implied warranty.
  15.  */
  16.  
  17. #ifndef lint
  18. static char rcsid[] = "$Header: /user6/ouster/wish/RCS/tkPlace.c,v 1.6 92/06/10 08:59:28 ouster Exp $ SPRITE (Berkeley)";
  19. #endif /* not lint */
  20.  
  21. #include "tkConfig.h"
  22. #include "tkInt.h"
  23.  
  24. /*
  25.  * Border modes for relative placement:
  26.  *
  27.  * BM_INSIDE:        relative distances computed using area inside
  28.  *            all borders of master window.
  29.  * BM_OUTSIDE:        relative distances computed using outside area
  30.  *            that includes all borders of master.
  31.  * BM_IGNORE:        border issues are ignored:  place relative to
  32.  *            master's actual window size.
  33.  */
  34.  
  35. typedef enum {BM_INSIDE, BM_OUTSIDE, BM_IGNORE} BorderMode;
  36.  
  37. /*
  38.  * For each window whose geometry is managed by the placer there is
  39.  * a structure of the following type:
  40.  */
  41.  
  42. typedef struct Slave {
  43.     Tk_Window tkwin;        /* Tk's token for window. */
  44.     struct Master *masterPtr;    /* Pointer to information for window
  45.                  * relative to which tkwin is placed.
  46.                  * This isn't necessarily the logical
  47.                  * parent of tkwin.  NULL means the
  48.                  * master was deleted or never assigned. */
  49.     struct Slave *nextPtr;    /* Next in list of windows placed relative
  50.                  * to same master (NULL for end of list). */
  51.  
  52.     /*
  53.      * Geometry information for window;  where there are both relative
  54.      * and absolute values for the same attribute (e.g. x and relX) only
  55.      * one of them is actually used, depending on flags.
  56.      */
  57.  
  58.     int x, y;            /* X and Y pixel coordinates for tkwin. */
  59.     float relX, relY;        /* X and Y coordinates relative to size of
  60.                  * master. */
  61.     int width, height;        /* Absolute dimensions for tkwin. */
  62.     float relWidth, relHeight;    /* Dimensions for tkwin relative to size of
  63.                  * master. */
  64.     Tk_Anchor anchor;        /* Which point on tkwin is placed at the
  65.                  * given position. */
  66.     BorderMode borderMode;    /* How to treat borders of master window. */
  67.     int flags;            /* Various flags;  see below for bit
  68.                  * definitions. */
  69. } Slave;
  70.  
  71. /*
  72.  * Flag definitions for Slave structures:
  73.  *
  74.  * CHILD_REL_X -        1 means use relX field;  0 means use x.
  75.  * CHILD_REL_Y -        1 means use relY field;  0 means use y;
  76.  * CHILD_WIDTH -        1 means use width field;
  77.  * CHILD_REL_WIDTH -        1 means use relWidth;  if neither this nor
  78.  *                CHILD_WIDTH is 1, use window's requested
  79.  *                width.
  80.  * CHILD_HEIGHT -        1 means use height field;
  81.  * CHILD_REL_HEIGHT -        1 means use relHeight;  if neither this nor
  82.  *                CHILD_HEIGHT is 1, use window's requested
  83.  *                height.
  84.  */
  85.  
  86. #define CHILD_REL_X        1
  87. #define CHILD_REL_Y        2
  88. #define CHILD_WIDTH        4
  89. #define CHILD_REL_WIDTH        8
  90. #define CHILD_HEIGHT        0x10
  91. #define CHILD_REL_HEIGHT    0x20
  92.  
  93. /*
  94.  * For each master window that has a slave managed by the placer there
  95.  * is a structure of the following form:
  96.  */
  97.  
  98. typedef struct Master {
  99.     Tk_Window tkwin;        /* Tk's token for master window. */
  100.     struct Slave *slavePtr;    /* First in linked list of slaves
  101.                  * placed relative to this master. */
  102.     int flags;            /* See below for bit definitions. */
  103. } Master;
  104.  
  105. /*
  106.  * Flag definitions for masters:
  107.  *
  108.  * PARENT_RECONFIG_PENDING -    1 means that a call to RecomputePlacement
  109.  *                is already pending via a Do_When_Idle handler.
  110.  */
  111.  
  112. #define PARENT_RECONFIG_PENDING    1
  113.  
  114. /*
  115.  * The hash tables below both use Tk_Window tokens as keys.  They map
  116.  * from Tk_Windows to Slave and Master structures for windows, if they
  117.  * exist.
  118.  */
  119.  
  120. static int initialized = 0;
  121. static Tcl_HashTable masterTable;
  122. static Tcl_HashTable slaveTable;
  123.  
  124. /*
  125.  * Forward declarations for procedures defined later in this file:
  126.  */
  127.  
  128. static void        SlaveStructureProc _ANSI_ARGS_((ClientData clientData,
  129.                 XEvent *eventPtr));
  130. static int        ConfigureSlave _ANSI_ARGS_((Tcl_Interp *interp,
  131.                 Slave *slavePtr, int argc, char **argv));
  132. static Slave *        FindSlave _ANSI_ARGS_((Tk_Window tkwin));
  133. static Master *        FindMaster _ANSI_ARGS_((Tk_Window tkwin));
  134. static void        MasterStructureProc _ANSI_ARGS_((ClientData clientData,
  135.                 XEvent *eventPtr));
  136. static void        PlaceRequestProc _ANSI_ARGS_((ClientData clientData,
  137.                 Tk_Window tkwin));
  138. static void        RecomputePlacement _ANSI_ARGS_((ClientData clientData));
  139. static void        UnlinkSlave _ANSI_ARGS_((Slave *slavePtr));
  140.  
  141. /*
  142.  *--------------------------------------------------------------
  143.  *
  144.  * Tk_PlaceCmd --
  145.  *
  146.  *    This procedure is invoked to process the "place" Tcl
  147.  *    commands.  See the user documentation for details on
  148.  *    what it does.
  149.  *
  150.  * Results:
  151.  *    A standard Tcl result.
  152.  *
  153.  * Side effects:
  154.  *    See the user documentation.
  155.  *
  156.  *--------------------------------------------------------------
  157.  */
  158.  
  159. int
  160. Tk_PlaceCmd(clientData, interp, argc, argv)
  161.     ClientData clientData;    /* Main window associated with interpreter. */
  162.     Tcl_Interp *interp;        /* Current interpreter. */
  163.     int argc;            /* Number of arguments. */
  164.     char **argv;        /* Argument strings. */
  165. {
  166.     Tk_Window tkwin;
  167.     Slave *slavePtr;
  168.     Tcl_HashEntry *hPtr;
  169.     int length;
  170.     char c;
  171.  
  172.     /*
  173.      * Initialize, if that hasn't been done yet.
  174.      */
  175.  
  176.     if (!initialized) {
  177.     Tcl_InitHashTable(&masterTable, TCL_ONE_WORD_KEYS);
  178.     Tcl_InitHashTable(&slaveTable, TCL_ONE_WORD_KEYS);
  179.     initialized = 1;
  180.     }
  181.  
  182.     if (argc < 3) {
  183.     Tcl_AppendResult(interp, "wrong # args: should be \"",
  184.         argv[0], " option|pathName args", (char *) NULL);
  185.     return TCL_ERROR;
  186.     }
  187.     c = argv[1][0];
  188.     length = strlen(argv[1]);
  189.  
  190.     /*
  191.      * Handle special shortcut where window name is first argument.
  192.      */
  193.  
  194.     if (c == '.') {
  195.     tkwin = Tk_NameToWindow(interp, argv[1], (Tk_Window) clientData);
  196.     if (tkwin == NULL) {
  197.         return TCL_ERROR;
  198.     }
  199.     slavePtr = FindSlave(tkwin);
  200.     return ConfigureSlave(interp, slavePtr, argc-2, argv+2);
  201.     }
  202.  
  203.     /*
  204.      * Handle more general case of option followed by window name followed
  205.      * by possible additional arguments.
  206.      */
  207.  
  208.     tkwin = Tk_NameToWindow(interp, argv[2], (Tk_Window) clientData);
  209.     if (tkwin == NULL) {
  210.     return TCL_ERROR;
  211.     }
  212.     if ((c == 'c') && (strncmp(argv[1], "configure", length) == 0)) {
  213.     if (argc < 5) {
  214.         Tcl_AppendResult(interp, "wrong # args: should be \"",
  215.             argv[0],
  216.             " configure pathName option value ?option value ...?\"",
  217.             (char *) NULL);
  218.         return TCL_ERROR;
  219.     }
  220.     slavePtr = FindSlave(tkwin);
  221.     return ConfigureSlave(interp, slavePtr, argc-3, argv+3);
  222.     } else if ((c == 'd') && (strncmp(argv[1], "dependents", length) == 0)) {
  223.     if (argc != 3) {
  224.         Tcl_AppendResult(interp, "wrong # args: should be \"",
  225.             argv[0], " dependents pathName\"", (char *) NULL);
  226.         return TCL_ERROR;
  227.     }
  228.     hPtr = Tcl_FindHashEntry(&masterTable, (char *) tkwin);
  229.     if (hPtr != NULL) {
  230.         Master *masterPtr;
  231.         masterPtr = (Master *) Tcl_GetHashValue(hPtr);
  232.         for (slavePtr = masterPtr->slavePtr; slavePtr != NULL;
  233.             slavePtr = slavePtr->nextPtr) {
  234.         Tcl_AppendElement(interp, Tk_PathName(slavePtr->tkwin), 0);
  235.         }
  236.     }
  237.     } else if ((c == 'f') && (strncmp(argv[1], "forget", length) == 0)) {
  238.     if (argc != 3) {
  239.         Tcl_AppendResult(interp, "wrong # args: should be \"",
  240.             argv[0], " forget pathName\"", (char *) NULL);
  241.         return TCL_ERROR;
  242.     }
  243.     hPtr = Tcl_FindHashEntry(&slaveTable, (char *) tkwin);
  244.     if (hPtr == NULL) {
  245.         return TCL_OK;
  246.     }
  247.     slavePtr = (Slave *) Tcl_GetHashValue(hPtr);
  248.     UnlinkSlave(slavePtr);
  249.     Tcl_DeleteHashEntry(hPtr);
  250.     Tk_DeleteEventHandler(tkwin, StructureNotifyMask, SlaveStructureProc,
  251.         (ClientData) slavePtr);
  252.     Tk_ManageGeometry(tkwin, (Tk_GeometryProc *) NULL, (ClientData) NULL);
  253.     Tk_UnmapWindow(tkwin);
  254.     ckfree((char *) slavePtr);
  255.     } else if ((c == 'i') && (strncmp(argv[1], "info", length) == 0)) {
  256.     char buffer[50];
  257.  
  258.     if (argc != 3) {
  259.         Tcl_AppendResult(interp, "wrong # args: should be \"",
  260.             argv[0], " info pathName\"", (char *) NULL);
  261.         return TCL_ERROR;
  262.     }
  263.     hPtr = Tcl_FindHashEntry(&slaveTable, (char *) tkwin);
  264.     if (hPtr == NULL) {
  265.         return TCL_OK;
  266.     }
  267.     slavePtr = (Slave *) Tcl_GetHashValue(hPtr);
  268.     if (slavePtr->flags & CHILD_REL_X) {
  269.         sprintf(buffer, "-relx %.4g", slavePtr->relX);
  270.     } else {
  271.         sprintf(buffer, "-x %d", slavePtr->x);
  272.     }
  273.     Tcl_AppendResult(interp, buffer, (char *) NULL);
  274.     if (slavePtr->flags & CHILD_REL_Y) {
  275.         sprintf(buffer, " -rely %.4g", slavePtr->relY);
  276.     } else {
  277.         sprintf(buffer, " -y %d", slavePtr->y);
  278.     }
  279.     Tcl_AppendResult(interp, buffer, (char *) NULL);
  280.     if (slavePtr->flags & CHILD_REL_WIDTH) {
  281.         sprintf(buffer, " -relwidth %.4g", slavePtr->relWidth);
  282.         Tcl_AppendResult(interp, buffer, (char *) NULL);
  283.     } else if (slavePtr->flags & CHILD_WIDTH) {
  284.         sprintf(buffer, " -width %d", slavePtr->width);
  285.         Tcl_AppendResult(interp, buffer, (char *) NULL);
  286.     }
  287.     if (slavePtr->flags & CHILD_REL_HEIGHT) {
  288.         sprintf(buffer, " -relheight %.4g", slavePtr->relHeight);
  289.         Tcl_AppendResult(interp, buffer, (char *) NULL);
  290.     } else if (slavePtr->flags & CHILD_HEIGHT) {
  291.         sprintf(buffer, " -height %d", slavePtr->height);
  292.         Tcl_AppendResult(interp, buffer, (char *) NULL);
  293.     }
  294.     Tcl_AppendResult(interp, " -anchor ", Tk_NameOfAnchor(slavePtr->anchor),
  295.         (char *) NULL);
  296.     if (slavePtr->borderMode == BM_OUTSIDE) {
  297.         Tcl_AppendResult(interp, " -bordermode outside", (char *) NULL);
  298.     } else if (slavePtr->borderMode == BM_IGNORE) {
  299.         Tcl_AppendResult(interp, " -bordermode ignore", (char *) NULL);
  300.     }
  301.     if ((slavePtr->masterPtr != NULL)
  302.         && (slavePtr->masterPtr->tkwin != Tk_Parent(slavePtr->tkwin))) {
  303.         Tcl_AppendResult(interp, " -in ",
  304.             Tk_PathName(slavePtr->masterPtr->tkwin), (char *) NULL);
  305.     }
  306.     } else {
  307.     Tcl_AppendResult(interp, "unknown or ambiguous option \"", argv[1],
  308.         "\": must be configure, dependents, forget, or info",
  309.         (char *) NULL);
  310.     return TCL_ERROR;
  311.     }
  312.     return TCL_OK;
  313. }
  314.  
  315. /*
  316.  *----------------------------------------------------------------------
  317.  *
  318.  * FindSlave --
  319.  *
  320.  *    Given a Tk_Window token, find the Slave structure corresponding
  321.  *    to that token (making a new one if necessary).
  322.  *
  323.  * Results:
  324.  *    None.
  325.  *
  326.  * Side effects:
  327.  *    A new Slave structure may be created.
  328.  *
  329.  *----------------------------------------------------------------------
  330.  */
  331.  
  332. static Slave *
  333. FindSlave(tkwin)
  334.     Tk_Window tkwin;        /* Token for desired slave. */
  335. {
  336.     Tcl_HashEntry *hPtr;
  337.     register Slave *slavePtr;
  338.     int new;
  339.  
  340.     hPtr = Tcl_CreateHashEntry(&slaveTable, (char *) tkwin, &new);
  341.     if (new) {
  342.     slavePtr = (Slave *) ckalloc(sizeof(Slave));
  343.     slavePtr->tkwin = tkwin;
  344.     slavePtr->masterPtr = NULL;
  345.     slavePtr->nextPtr = NULL;
  346.     slavePtr->x = slavePtr->y = 0;
  347.     slavePtr->relX = slavePtr->relY = 0.0;
  348.     slavePtr->width = slavePtr->height = 0;
  349.     slavePtr->relWidth = slavePtr->relHeight = 0.0;
  350.     slavePtr->anchor = TK_ANCHOR_NW;
  351.     slavePtr->borderMode = BM_INSIDE;
  352.     slavePtr->flags = 0;
  353.     Tcl_SetHashValue(hPtr, slavePtr);
  354.     Tk_CreateEventHandler(tkwin, StructureNotifyMask, SlaveStructureProc,
  355.         (ClientData) slavePtr);
  356.     Tk_ManageGeometry(tkwin, PlaceRequestProc, (ClientData) slavePtr);
  357.     } else {
  358.     slavePtr = (Slave *) Tcl_GetHashValue(hPtr);
  359.     }
  360.     return slavePtr;
  361. }
  362.  
  363. /*
  364.  *----------------------------------------------------------------------
  365.  *
  366.  * UnlinkSlave --
  367.  *
  368.  *    This procedure removes a slave window from the chain of slaves
  369.  *    in its master.
  370.  *
  371.  * Results:
  372.  *    None.
  373.  *
  374.  * Side effects:
  375.  *    The slave list of slavePtr's master changes.
  376.  *
  377.  *----------------------------------------------------------------------
  378.  */
  379.  
  380. static void
  381. UnlinkSlave(slavePtr)
  382.     Slave *slavePtr;        /* Slave structure to be unlinked. */
  383. {
  384.     register Master *masterPtr;
  385.     register Slave *prevPtr;
  386.  
  387.     masterPtr = slavePtr->masterPtr;
  388.     if (masterPtr == NULL) {
  389.     return;
  390.     }
  391.     if (masterPtr->slavePtr == slavePtr) {
  392.     masterPtr->slavePtr = slavePtr->nextPtr;
  393.     } else {
  394.     for (prevPtr = masterPtr->slavePtr; ;
  395.         prevPtr = prevPtr->nextPtr) {
  396.         if (prevPtr == NULL) {
  397.         panic("UnlinkSlave couldn't find slave to unlink");
  398.         }
  399.         if (prevPtr->nextPtr == slavePtr) {
  400.         prevPtr->nextPtr = slavePtr->nextPtr;
  401.         break;
  402.         }
  403.     }
  404.     }
  405.     slavePtr->masterPtr = NULL;
  406. }
  407.  
  408. /*
  409.  *----------------------------------------------------------------------
  410.  *
  411.  * FindMaster --
  412.  *
  413.  *    Given a Tk_Window token, find the Master structure corresponding
  414.  *    to that token (making a new one if necessary).
  415.  *
  416.  * Results:
  417.  *    None.
  418.  *
  419.  * Side effects:
  420.  *    A new Master structure may be created.
  421.  *
  422.  *----------------------------------------------------------------------
  423.  */
  424.  
  425. static Master *
  426. FindMaster(tkwin)
  427.     Tk_Window tkwin;        /* Token for desired master. */
  428. {
  429.     Tcl_HashEntry *hPtr;
  430.     register Master *masterPtr;
  431.     int new;
  432.  
  433.     hPtr = Tcl_CreateHashEntry(&masterTable, (char *) tkwin, &new);
  434.     if (new) {
  435.     masterPtr = (Master *) ckalloc(sizeof(Master));
  436.     masterPtr->tkwin = tkwin;
  437.     masterPtr->slavePtr = NULL;
  438.     masterPtr->flags = 0;
  439.     Tcl_SetHashValue(hPtr, masterPtr);
  440.     Tk_CreateEventHandler(masterPtr->tkwin, StructureNotifyMask,
  441.         MasterStructureProc, (ClientData) masterPtr);
  442.     } else {
  443.     masterPtr = (Master *) Tcl_GetHashValue(hPtr);
  444.     }
  445.     return masterPtr;
  446. }
  447.  
  448. /*
  449.  *----------------------------------------------------------------------
  450.  *
  451.  * ConfigureSlave --
  452.  *
  453.  *    This procedure is called to process an argv/argc list to
  454.  *    reconfigure the placement of a window.
  455.  *
  456.  * Results:
  457.  *    A standard Tcl result.  If an error occurs then a message is
  458.  *    left in interp->result.
  459.  *
  460.  * Side effects:
  461.  *    Information in slavePtr may change, and slavePtr's master is
  462.  *    scheduled for reconfiguration.
  463.  *
  464.  *----------------------------------------------------------------------
  465.  */
  466.  
  467. static int
  468. ConfigureSlave(interp, slavePtr, argc, argv)
  469.     Tcl_Interp *interp;        /* Used for error reporting. */
  470.     Slave *slavePtr;        /* Pointer to current information
  471.                  * about slave. */
  472.     int argc;            /* Number of config arguments. */
  473.     char **argv;        /* String values for arguments. */
  474. {
  475.     register Master *masterPtr;
  476.     int c, length, result;
  477.     double d;
  478.  
  479.     result = TCL_OK;
  480.     for ( ; argc > 0; argc -= 2, argv += 2) {
  481.     if (argc < 2) {
  482.         Tcl_AppendResult(interp, "extra option \"", argv[0],
  483.             "\" (option with no value?)", (char *) NULL);
  484.         result = TCL_ERROR;
  485.         goto done;
  486.     }
  487.     length = strlen(argv[0]);
  488.     c = argv[0][1];
  489.     if ((c == 'a') && (strncmp(argv[0], "-anchor", length) == 0)) {
  490.         if (Tk_GetAnchor(interp, argv[1], &slavePtr->anchor) != TCL_OK) {
  491.         result = TCL_ERROR;
  492.         goto done;
  493.         }
  494.     } else if ((c == 'b')
  495.         && (strncmp(argv[0], "-bordermode", length) == 0)) {
  496.         c = argv[1][0];
  497.         length = strlen(argv[1]);
  498.         if ((c == 'i') && (strncmp(argv[1], "ignore", length) == 0)
  499.             && (length >= 2)) {
  500.         slavePtr->borderMode = BM_IGNORE;
  501.         } else if ((c == 'i') && (strncmp(argv[1], "inside", length) == 0)
  502.             && (length >= 2)) {
  503.         slavePtr->borderMode = BM_INSIDE;
  504.         } else if ((c == 'o')
  505.             && (strncmp(argv[1], "outside", length) == 0)) {
  506.         slavePtr->borderMode = BM_OUTSIDE;
  507.         } else {
  508.         Tcl_AppendResult(interp, "bad border mode \"", argv[1],
  509.             "\": must be ignore, inside, or outside",
  510.             (char *) NULL);
  511.         result = TCL_ERROR;
  512.         goto done;
  513.         }
  514.     } else if ((c == 'h') && (strncmp(argv[0], "-height", length) == 0)) {
  515.         if (argv[1][0] == 0) {
  516.         slavePtr->flags &= ~(CHILD_REL_HEIGHT|CHILD_HEIGHT);
  517.         } else {
  518.         if (Tk_GetPixels(interp, slavePtr->tkwin, argv[1],
  519.             &slavePtr->height) != TCL_OK) {
  520.             result = TCL_ERROR;
  521.             goto done;
  522.         }
  523.         slavePtr->flags &= ~CHILD_REL_HEIGHT;
  524.         slavePtr->flags |= CHILD_HEIGHT;
  525.         }
  526.     } else if ((c == 'i') && (strncmp(argv[0], "-in", length) == 0)) {
  527.         Tk_Window tkwin;
  528.         Tk_Window ancestor;
  529.  
  530.         tkwin = Tk_NameToWindow(interp, argv[1], slavePtr->tkwin);
  531.         if (tkwin == NULL) {
  532.         result = TCL_ERROR;
  533.         goto done;
  534.         }
  535.  
  536.         /*
  537.          * Make sure that the new master is either the logical parent
  538.          * of the slave or a descendant of that window.
  539.          */
  540.  
  541.         for (ancestor = tkwin; ; ancestor = Tk_Parent(ancestor)) {
  542.         if (ancestor == Tk_Parent(slavePtr->tkwin)) {
  543.             break;
  544.         }
  545.         if (((Tk_FakeWin *) (ancestor))->flags & TK_TOP_LEVEL) {
  546.             Tcl_AppendResult(interp, "can't place ",
  547.                 Tk_PathName(slavePtr->tkwin), " relative to ",
  548.                 Tk_PathName(tkwin), (char *) NULL);
  549.             result = TCL_ERROR;
  550.             goto done;
  551.         }
  552.         }
  553.         UnlinkSlave(slavePtr);
  554.         slavePtr->masterPtr = FindMaster(tkwin);
  555.         slavePtr->nextPtr = slavePtr->masterPtr->slavePtr;
  556.         slavePtr->masterPtr->slavePtr = slavePtr;
  557.     } else if ((c == 'r') && (strncmp(argv[0], "-relheight", length) == 0)
  558.         && (length >= 5)) {
  559.         if (Tcl_GetDouble(interp, argv[1], &d) != TCL_OK) {
  560.         result = TCL_ERROR;
  561.         goto done;
  562.         }
  563.         slavePtr->relHeight = d;
  564.         slavePtr->flags |= CHILD_REL_HEIGHT;
  565.         slavePtr->flags &= ~CHILD_HEIGHT;
  566.     } else if ((c == 'r') && (strncmp(argv[0], "-relwidth", length) == 0)
  567.         && (length >= 5)) {
  568.         if (Tcl_GetDouble(interp, argv[1], &d) != TCL_OK) {
  569.         result = TCL_ERROR;
  570.         goto done;
  571.         }
  572.         slavePtr->relWidth = d;
  573.         slavePtr->flags |= CHILD_REL_WIDTH;
  574.         slavePtr->flags &= ~CHILD_WIDTH;
  575.     } else if ((c == 'r') && (strncmp(argv[0], "-relx", length) == 0)
  576.         && (length >= 5)) {
  577.         if (Tcl_GetDouble(interp, argv[1], &d) != TCL_OK) {
  578.         result = TCL_ERROR;
  579.         goto done;
  580.         }
  581.         slavePtr->relX = d;
  582.         slavePtr->flags |= CHILD_REL_X;
  583.     } else if ((c == 'r') && (strncmp(argv[0], "-rely", length) == 0)
  584.         && (length >= 5)) {
  585.         if (Tcl_GetDouble(interp, argv[1], &d) != TCL_OK) {
  586.         result = TCL_ERROR;
  587.         goto done;
  588.         }
  589.         slavePtr->relY = d;
  590.         slavePtr->flags |= CHILD_REL_Y;
  591.     } else if ((c == 'w') && (strncmp(argv[0], "-width", length) == 0)) {
  592.         if (argv[1][0] == 0) {
  593.         slavePtr->flags &= ~(CHILD_REL_WIDTH|CHILD_WIDTH);
  594.         } else {
  595.         if (Tk_GetPixels(interp, slavePtr->tkwin, argv[1],
  596.             &slavePtr->width) != TCL_OK) {
  597.             result = TCL_ERROR;
  598.             goto done;
  599.         }
  600.         slavePtr->flags &= ~CHILD_REL_WIDTH;
  601.         slavePtr->flags |= CHILD_WIDTH;
  602.         }
  603.     } else if ((c == 'x') && (strncmp(argv[0], "-x", length) == 0)) {
  604.         if (Tk_GetPixels(interp, slavePtr->tkwin, argv[1],
  605.             &slavePtr->x) != TCL_OK) {
  606.         result = TCL_ERROR;
  607.         goto done;
  608.         }
  609.         slavePtr->flags &= ~CHILD_REL_X;
  610.     } else if ((c == 'y') && (strncmp(argv[0], "-y", length) == 0)) {
  611.         if (Tk_GetPixels(interp, slavePtr->tkwin, argv[1],
  612.             &slavePtr->y) != TCL_OK) {
  613.         result = TCL_ERROR;
  614.         goto done;
  615.         }
  616.         slavePtr->flags &= ~CHILD_REL_Y;
  617.     } else {
  618.         Tcl_AppendResult(interp, "unknown or ambiguous option \"",
  619.             argv[0], "\": must be -anchor, -bordermode, -height, ",
  620.             "-in, -relheight, -relwidth, -relx, -rely, -width, ",
  621.             "-x, or -y", (char *) NULL);
  622.         result = TCL_ERROR;
  623.         goto done;
  624.     }
  625.     }
  626.  
  627.     /*
  628.      * If there's no master specified for this slave, use its Tk_Parent.
  629.      * Then arrange for a placement recalculation in the master.
  630.      */
  631.  
  632.     done:
  633.     masterPtr = slavePtr->masterPtr;
  634.     if (masterPtr == NULL) {
  635.     masterPtr = FindMaster(Tk_Parent(slavePtr->tkwin));
  636.     slavePtr->masterPtr = masterPtr;
  637.     slavePtr->nextPtr = masterPtr->slavePtr;
  638.     masterPtr->slavePtr = slavePtr;
  639.     }
  640.     if (!(masterPtr->flags & PARENT_RECONFIG_PENDING)) {
  641.     masterPtr->flags |= PARENT_RECONFIG_PENDING;
  642.     Tk_DoWhenIdle(RecomputePlacement, (ClientData) masterPtr);
  643.     }
  644.     return result;
  645. }
  646.  
  647. /*
  648.  *----------------------------------------------------------------------
  649.  *
  650.  * RecomputePlacement --
  651.  *
  652.  *    This procedure is called as a when-idle handler.  It recomputes
  653.  *    the geometries of all the slaves of a given master.
  654.  *
  655.  * Results:
  656.  *    None.
  657.  *
  658.  * Side effects:
  659.  *    Windows may change size or shape.
  660.  *
  661.  *----------------------------------------------------------------------
  662.  */
  663.  
  664. static void
  665. RecomputePlacement(clientData)
  666.     ClientData clientData;    /* Pointer to Master record. */
  667. {
  668.     register Master *masterPtr = (Master *) clientData;
  669.     register Slave *slavePtr;
  670.     Tk_Window ancestor, realMaster;
  671.     int x, y, width, height;
  672.     int masterWidth, masterHeight, masterBW;
  673.  
  674.     masterPtr->flags &= ~PARENT_RECONFIG_PENDING;
  675.  
  676.     /*
  677.      * Iterate over all the slaves for the master.  Each slave's
  678.      * geometry can be computed independently of the other slaves.
  679.      */
  680.  
  681.     for (slavePtr = masterPtr->slavePtr; slavePtr != NULL;
  682.         slavePtr = slavePtr->nextPtr) {
  683.     /*
  684.      * Step 1: compute size and borderwidth of master, taking into
  685.      * account desired border mode.
  686.      */
  687.  
  688.     masterBW = 0;
  689.     masterWidth = Tk_Width(masterPtr->tkwin);
  690.     masterHeight = Tk_Height(masterPtr->tkwin);
  691.     if (slavePtr->borderMode == BM_INSIDE) {
  692.         masterBW = Tk_InternalBorderWidth(masterPtr->tkwin);
  693.     } else if (slavePtr->borderMode == BM_OUTSIDE) {
  694.         masterBW = -Tk_Changes(masterPtr->tkwin)->border_width;
  695.     }
  696.     masterWidth -= 2*masterBW;
  697.     masterHeight -= 2*masterBW;
  698.  
  699.     /*
  700.      * Step 2:  compute size of slave (outside dimensions including
  701.      * border) and location of anchor point within master.
  702.      */
  703.  
  704.     x = slavePtr->x;
  705.     if (slavePtr->flags & CHILD_REL_X) {
  706.         x = (slavePtr->relX*masterWidth) +
  707.         ((slavePtr->relX > 0) ? 0.5 : -0.5);
  708.     }
  709.     x += masterBW;
  710.     y = slavePtr->y;
  711.     if (slavePtr->flags & CHILD_REL_Y) {
  712.         y = (slavePtr->relY*masterHeight) +
  713.         ((slavePtr->relY > 0) ? 0.5 : -0.5);
  714.     }
  715.     y += masterBW;
  716.     if (slavePtr->flags & CHILD_REL_WIDTH) {
  717.         width = (slavePtr->relWidth*masterWidth) + 0.5;
  718.     } else if (slavePtr->flags & CHILD_WIDTH) {
  719.         width = slavePtr->width;
  720.     } else {
  721.         width = Tk_ReqWidth(slavePtr->tkwin)
  722.             + 2*Tk_Changes(slavePtr->tkwin)->border_width;
  723.     }
  724.     if (slavePtr->flags & CHILD_REL_HEIGHT) {
  725.         height = (slavePtr->relHeight*masterHeight) + 0.5;
  726.     } else if (slavePtr->flags & CHILD_HEIGHT) {
  727.         height = slavePtr->height;
  728.     } else {
  729.         height = Tk_ReqHeight(slavePtr->tkwin)
  730.             + 2*Tk_Changes(slavePtr->tkwin)->border_width;
  731.     }
  732.  
  733.     /*
  734.      * Step 3: adjust the x and y positions so that the desired
  735.      * anchor point on the slave appears at that position.  Also
  736.      * adjust for the border mode and master's border.
  737.      */
  738.  
  739.     switch (slavePtr->anchor) {
  740.         case TK_ANCHOR_N:
  741.         x -= width/2;
  742.         break;
  743.         case TK_ANCHOR_NE:
  744.         x -= width;
  745.         break;
  746.         case TK_ANCHOR_E:
  747.         x -= width;
  748.         y -= height/2;
  749.         break;
  750.         case TK_ANCHOR_SE:
  751.         x -= width;
  752.         y -= height;
  753.         break;
  754.         case TK_ANCHOR_S:
  755.         x -= width/2;
  756.         y -= height;
  757.         break;
  758.         case TK_ANCHOR_SW:
  759.         y -= height;
  760.         break;
  761.         case TK_ANCHOR_W:
  762.         y -= height/2;
  763.         break;
  764.         case TK_ANCHOR_NW:
  765.         break;
  766.         case TK_ANCHOR_CENTER:
  767.         x -= width/2;
  768.         y -= height/2;
  769.         break;
  770.     }
  771.  
  772.     /*
  773.      * Step 4: if masterPtr isn't actually the X master of slavePtr,
  774.      * then translate the x and y coordinates back into the coordinate
  775.      * system of masterPtr.
  776.      */
  777.  
  778.     for (ancestor = masterPtr->tkwin,
  779.         realMaster = Tk_Parent(slavePtr->tkwin);
  780.         ancestor != realMaster; ancestor = Tk_Parent(ancestor)) {
  781.         x += Tk_X(ancestor) + Tk_Changes(ancestor)->border_width;
  782.         y += Tk_Y(ancestor) + Tk_Changes(ancestor)->border_width;
  783.     }
  784.  
  785.     /*
  786.      * Step 5: adjust width and height again to reflect inside dimensions
  787.      * of window rather than outside.  Also make sure that the width and
  788.      * height aren't zero.
  789.      */
  790.  
  791.     width -= 2*Tk_Changes(slavePtr->tkwin)->border_width;
  792.     height -= 2*Tk_Changes(slavePtr->tkwin)->border_width;
  793.     if (width <= 0) {
  794.         width = 1;
  795.     }
  796.     if (height <= 0) {
  797.         height = 1;
  798.     }
  799.  
  800.     /*
  801.      * Step 6: see if the window's size or location has changed;  if
  802.      * so then tell X to reconfigure it.
  803.      */
  804.  
  805.     if ((x != Tk_X(slavePtr->tkwin))
  806.         || (y != Tk_Y(slavePtr->tkwin))
  807.         || (width != Tk_Width(slavePtr->tkwin))
  808.         || (height != Tk_Height(slavePtr->tkwin))) {
  809.         Tk_MoveResizeWindow(slavePtr->tkwin, x, y,
  810.             (unsigned int) width, (unsigned int) height);
  811.     }
  812.     Tk_MapWindow(slavePtr->tkwin);
  813.     }
  814. }
  815.  
  816. /*
  817.  *----------------------------------------------------------------------
  818.  *
  819.  * MasterStructureProc --
  820.  *
  821.  *    This procedure is invoked by the Tk event handler when
  822.  *    StructureNotify events occur for a master window.
  823.  *
  824.  * Results:
  825.  *    None.
  826.  *
  827.  * Side effects:
  828.  *    Structures get cleaned up if the window was deleted.  If the
  829.  *    window was resized then slave geometries get recomputed.
  830.  *
  831.  *----------------------------------------------------------------------
  832.  */
  833.  
  834. static void
  835. MasterStructureProc(clientData, eventPtr)
  836.     ClientData clientData;    /* Pointer to Master structure for window
  837.                  * referred to by eventPtr. */
  838.     XEvent *eventPtr;        /* Describes what just happened. */
  839. {
  840.     register Master *masterPtr = (Master *) clientData;
  841.     register Slave *slavePtr, *nextPtr;
  842.  
  843.     if (eventPtr->type == ConfigureNotify) {
  844.     if ((masterPtr->slavePtr != NULL)
  845.         && !(masterPtr->flags & PARENT_RECONFIG_PENDING)) {
  846.         masterPtr->flags |= PARENT_RECONFIG_PENDING;
  847.         Tk_DoWhenIdle(RecomputePlacement, (ClientData) masterPtr);
  848.     }
  849.     } else if (eventPtr->type == DestroyNotify) {
  850.     for (slavePtr = masterPtr->slavePtr; slavePtr != NULL;
  851.         slavePtr = nextPtr) {
  852.         slavePtr->masterPtr = NULL;
  853.         nextPtr = slavePtr->nextPtr;
  854.         slavePtr->nextPtr = NULL;
  855.     }
  856.     Tcl_DeleteHashEntry(Tcl_FindHashEntry(&masterTable,
  857.         (char *) masterPtr->tkwin));
  858.     if (masterPtr->flags & PARENT_RECONFIG_PENDING) {
  859.         Tk_CancelIdleCall(RecomputePlacement, (ClientData) masterPtr);
  860.     }
  861.     masterPtr->tkwin = NULL;
  862.     ckfree((char *) masterPtr);
  863.     }
  864. }
  865.  
  866. /*
  867.  *----------------------------------------------------------------------
  868.  *
  869.  * SlaveStructureProc --
  870.  *
  871.  *    This procedure is invoked by the Tk event handler when
  872.  *    StructureNotify events occur for a slave window.
  873.  *
  874.  * Results:
  875.  *    None.
  876.  *
  877.  * Side effects:
  878.  *    Structures get cleaned up if the window was deleted.
  879.  *
  880.  *----------------------------------------------------------------------
  881.  */
  882.  
  883. static void
  884. SlaveStructureProc(clientData, eventPtr)
  885.     ClientData clientData;    /* Pointer to Slave structure for window
  886.                  * referred to by eventPtr. */
  887.     XEvent *eventPtr;        /* Describes what just happened. */
  888. {
  889.     register Slave *slavePtr = (Slave *) clientData;
  890.  
  891.     if (eventPtr->type == DestroyNotify) {
  892.     UnlinkSlave(slavePtr);
  893.     Tcl_DeleteHashEntry(Tcl_FindHashEntry(&slaveTable,
  894.         (char *) slavePtr->tkwin));
  895.     ckfree((char *) slavePtr);
  896.     }
  897. }
  898.  
  899. /*
  900.  *----------------------------------------------------------------------
  901.  *
  902.  * PlaceRequestProc --
  903.  *
  904.  *    This procedure is invoked by Tk whenever a slave managed by us
  905.  *    changes its requested geometry.
  906.  *
  907.  * Results:
  908.  *    None.
  909.  *
  910.  * Side effects:
  911.  *    The window will get relayed out, if its requested size has
  912.  *    anything to do with its actual size.
  913.  *
  914.  *----------------------------------------------------------------------
  915.  */
  916.  
  917.     /* ARGSUSED */
  918. static void
  919. PlaceRequestProc(clientData, tkwin)
  920.     ClientData clientData;        /* Pointer to our record for slave. */
  921.     Tk_Window tkwin;            /* Window that changed its desired
  922.                      * size. */
  923. {
  924.     Slave *slavePtr = (Slave *) clientData;
  925.     Master *masterPtr;
  926.  
  927.     if (((slavePtr->flags & (CHILD_WIDTH|CHILD_REL_WIDTH)) != 0)
  928.         && ((slavePtr->flags & (CHILD_HEIGHT|CHILD_REL_HEIGHT)) != 0)) {
  929.     return;
  930.     }
  931.     masterPtr = slavePtr->masterPtr;
  932.     if (masterPtr == NULL) {
  933.     return;
  934.     }
  935.     if (!(masterPtr->flags & PARENT_RECONFIG_PENDING)) {
  936.     masterPtr->flags |= PARENT_RECONFIG_PENDING;
  937.     Tk_DoWhenIdle(RecomputePlacement, (ClientData) masterPtr);
  938.     }
  939. }
  940.